#include "xml_filetype.h"

int network::xml_filetype::_num_filetypes;
boost::mutex network::xml_filetype::_xml_mutex;

network::xml_filetype::xml_filetype()
:_buff(""), _start_tag(""), _end_in_start(false) 
{
	boost::mutex::scoped_lock l(_xml_mutex);
	if(_num_filetypes == 0) {
		xmlInitParser();
	}
	_num_filetypes++;
}

network::xml_filetype::~xml_filetype()
{
	boost::mutex::scoped_lock l(_xml_mutex);
	_num_filetypes--;
	if(_num_filetypes == 0) {
		xmlCleanupParser();
	}
}

boost::shared_ptr<model::dom> network::xml_filetype::parse(const string &msg)
{
	_buff.append(msg.c_str(), msg.size());
	if(_start_tag == "") {
		boost::regex expr("<([a-zA-Z]*).*?(/>|>).*");
		boost::cmatch what;
		boost::regex_match(msg.c_str(), what, expr);
		_start_tag = what[1];
		_end_in_start = what[2] == "/>";
	}
	
	// check if end-tag is included in the message...
	if(!_end_in_start) {
		string end_tag = "</" + _start_tag + ">";
		if(msg.find(end_tag) == std::string::npos) {
			throw parse_exception("Parse Error: " + msg);
		}
	}

	xmlDocPtr doc = xmlReadMemory(_buff.c_str(), _buff.size(), 
		"noname.xml", "ISO-8859-1", 0);
	if(doc) {
		_buff = "";
		_start_tag = "";
		boost::shared_ptr<model::dom> node(convert(doc));
		xmlFreeDoc(doc);
		return node;
	} else {
		cout << endl << endl << "throwing-parse-Exception" << endl;
		xmlFreeDoc(doc); // watch out for teh evil memory leaks!!11
		throw parse_exception("Parse Error: " + msg);
	}
	// you never reach this code:
	return boost::shared_ptr<model::dom>();
}

boost::shared_ptr<model::dom> network::xml_filetype::convert(xmlDocPtr ptr)
{
	// convert the libxml2 node-structure to our own structure
	xmlNode *root_element = xmlDocGetRootElement(ptr);
	if(root_element->type != XML_ELEMENT_NODE) {
		return boost::shared_ptr<model::dom>();
	}
	boost::shared_ptr<model::dom> node(new model::dom(
		(const char*)root_element->name));
	conv_attrib(root_element->properties, node);
	recur_conv(root_element->children, node);
	return node;
}

void network::xml_filetype::recur_conv(xmlNode *ptr,
					boost::shared_ptr<model::dom> parent)
{
	boost::shared_ptr<model::dom> arg(parent);
	for(; ptr; ptr = ptr->next) {
		parent = arg;
		if(ptr->type == XML_ELEMENT_NODE) {
			boost::shared_ptr<model::dom> n(
				parent->insert((const char*) ptr->name)
			);
			// converting the attributes:
			conv_attrib(ptr->properties, n);

			parent = n;
		} else if(ptr->type == XML_TEXT_NODE) {
			parent->insert_cdata((const char*)ptr->content);
		}
		recur_conv(ptr->children, parent);
	}
}

void network::xml_filetype::conv_attrib(xmlAttr *attr, 
					boost::shared_ptr<model::dom> node)
{
	string name, val;
	for(; attr; attr = attr->next) {
		const char *tmp = (const char*)
			xmlGetProp(attr->parent, attr->name);
		val = tmp;
		name = (const char*)attr->name;
		node->insert_attrib(name, val);
		free((void*)tmp);
	}
}

string network::xml_filetype::send(boost::shared_ptr<model::dom> dom) 
{
	return dom->to_string();
}
